package dcc.shannonfano;

import java.io.FileOutputStream;

/**
 * The Class Decoder.
 */
public class Decoder implements IShannonFano {

	/** The output filename. */
	private String fileName, outputFilename;
	
	/** The h codes. */
	private String[] hCodes = new String[MAXCHARS];
	
	/** The distinct chars. */
	private int distinctChars = 0;
	
	/** The output filelen. */
	private long fileLen = 0, outputFilelen;

	/** The outf. */
	private FileOutputStream outf;
	
	/** The g summary. */
	private String gSummary;

	/**
	 * Instantiates a new decoder.
	 */
	public Decoder() {
		loadFile("", "");
	}

	/**
	 * Instantiates a new decoder.
	 *
	 * @param txt the txt
	 */
	public Decoder(String txt) {
		loadFile(txt);
	}

	/**
	 * Instantiates a new decoder.
	 *
	 * @param txt the txt
	 * @param txt2 the txt2
	 */
	public Decoder(String txt, String txt2) {
		loadFile(txt, txt2);
	}

	/**
	 * Load file.
	 *
	 * @param txt the txt
	 */
	public void loadFile(String txt) {
		fileName = txt;
		outputFilename = stripExtension(txt, strExtension);
		gSummary = "";
	}

	/**
	 * Load file.
	 *
	 * @param txt the txt
	 * @param txt2 the txt2
	 */
	public void loadFile(String txt, String txt2) {
		fileName = txt;
		outputFilename = txt2;
		gSummary = "";
	}

	/**
	 * Strip extension.
	 *
	 * @param ff the ff
	 * @param ext the ext
	 * @return the string
	 */
	String stripExtension(String ff, String ext) {
		ff = ff.toLowerCase();
		if (ff.endsWith(ext.toLowerCase())) {
			return ff.substring(0, ff.length() - ext.length());
		}
		return "_" + ff;
	}

	/**
	 * Decode file.
	 *
	 * @return true, if successful
	 * @throws Exception the exception
	 */
	public boolean decodeFile() throws Exception {

		if (fileName.length() == 0)
			return false;

		for (int i = 0; i < MAXCHARS; i++)
			hCodes[i] = "";
		long i;
		BitReader fin = new BitReader(fileName);
		fileLen = fin.available();

		String buf;
		buf = fin.getBytes(sfSignature.length());

		if (!buf.equals(sfSignature))
			return false;
		outputFilelen = Long.parseLong(fin.getBits(32), 2);
		distinctChars = Integer.parseInt(fin.getBits(8), 2) + 1;
		gSummary += ("Compressed File Size : " + fileLen + "\n");
		gSummary += ("Original   File Size : " + outputFilelen + "\n");
		gSummary += ("Distinct   Chars     : " + distinctChars + "\n");
		for (i = 0; i < distinctChars; i++) {

			int ch = Integer.parseInt(fin.getBits(8), 2);
			int len = Integer.parseInt(leftPadder(fin.getBits(5), 8), 2);
			hCodes[ch] = fin.getBits(len);
			// System.out.println((char)ch + " : " + hCodes[ch]);
		}

		try {

			outf = new FileOutputStream(outputFilename);
			i = 0;
			int k;
			int ch;

			while (i < outputFilelen) {
				buf = "";
				for (k = 0; k < 32; k++) {
					buf += fin.getBit();
					ch = findCodeword(buf);
					if (ch > -1) {
						outf.write((char) ch);
						buf = "";
						i++;
						break;
					}
				}
				if (k >= 32)
					throw new Exception(gSummary + "\nCorrupted File!\n");

			}

			outf.close();
			return true;

		} catch (Exception e) {
			throw e;
		}

	}

	/**
	 * Find codeword.
	 *
	 * @param cw the cw
	 * @return the int
	 */
	int findCodeword(String cw) {
		int ret = -1;
		for (int i = 0; i < MAXCHARS; i++) {
			if (hCodes[i] != "" && cw.equals(hCodes[i])) {
				ret = i;
				break;
			}
		}
		return ret;
	}

	/**
	 * Left padder.
	 *
	 * @param txt the txt
	 * @param n the n
	 * @return the string
	 */
	String leftPadder(String txt, int n) {
		while (txt.length() < n)
			txt = "0" + txt;
		return txt;
	}

	/**
	 * Right padder.
	 *
	 * @param txt the txt
	 * @param n the n
	 * @return the string
	 */
	String rightPadder(String txt, int n) {
		while (txt.length() < n)
			txt += "0";
		return txt;
	}

	/**
	 * Gets the summary.
	 *
	 * @return the summary
	 */
	public String getSummary() {
		return gSummary;
	}

}
